package pl.enigmatic.manage;

import java.util.List;
import java.util.Vector;

/**
 * copied from jme3's own AppStateManager
 * @author reggie_7
 */
public class Manager<C, M extends Managed<C, Mgr>, Mgr extends Manager<C, M, Mgr>> {

	@SuppressWarnings("unchecked")
	private final Mgr mgr = (Mgr) this;
	/**
	 *  Holds the active states once they are initialized.  
	 */
	private final List<M> states = new Vector<M>();
	// All of the above lists need to be thread safe but access will be
	// synchronized separately.... but always on the states list.  This
	// is to avoid deadlocking that may occur and the most common use case
	// is that they are all modified from the same thread anyway.
	protected final Class<M> managed;
	protected final C context;

	public Manager(Class<M> managed, C app) {
		this.context = app;
		this.managed = managed;
	}

	/**
	 * Attach a state to the AppStateManager, the same state cannot be attached
	 * twice.
	 *
	 * @param state The state to attach
	 * @return True if the state was successfully attached, false if the state
	 * was already attached.
	 */
	public boolean attach(M state) {
		synchronized (states) {
			if (!states.contains(state)) {
				state.stateAttached(mgr);
				return true;
			} else {
				return false;
			}
		}
	}

	/**
	 * Detaches the state from the AppStateManager. 
	 *
	 * @param state The state to detach
	 * @return True if the state was detached successfully, false
	 * if the state was not attached in the first place.
	 */
	public boolean detach(M state) {
		synchronized (states) {
			if (states.contains(state)) {
				state.stateDetached(mgr);
				states.remove(state);
				return true;
			} else {
				return false;
			}
		}
	}

	/**
	 * Check if a state is attached or not.
	 *
	 * @param state The state to check
	 * @return True if the state is currently attached to this AppStateManager.
	 * 
	 * @see AppStateManager#attach(com.jme3.app.state.AppState)
	 */
	public boolean hasState(M state) {
		synchronized (states) {
			return states.contains(state);
		}
	}

	/**
	 * Returns the first state that is an instance of subclass of the specified class.
	 * @param <T>
	 * @param stateClass
	 * @return First attached state that is an instance of stateClass
	 */
	@SuppressWarnings("unchecked")
	public <T extends M> T getState(Class<T> stateClass) {
		synchronized (states) {
			for (M state : states) {
				if (stateClass.isAssignableFrom(state.getClass())) {
					return (T) state;
				}
			}
		}
		return null;
	}

	public C getContext() {
		return context;
	}
}
